package solver

import (
	"fmt"
	"gosudoku/set"
	"log"
	"math/rand"
	"slices"
	"strings"
	"time"
)

/*
op:
* 0, no limits
* 1, eq
* 2, neq
* 3, has
*/
type CondCell struct {
	op    int
	cands set.Set
}

func (cc *CondCell) setCond(op int, values []int) {
	cc.op = op
	cc.cands.Empty()
	for _, value := range values {
		cc.cands.Add(toCand(value))
	}
}

func (cc *CondCell) getCands() []int {
	return cc.cands.GetList()
}
func (cc *CondCell) getValues() []int {
	cands := cc.cands.GetList()
	values := make([]int, len(cands))
	for i, cand := range cands {
		values[i] = toValue(cand)
	}
	return values
}

func (cc CondCell) String() string {
	var b strings.Builder
	switch cc.op {
	case 1:
		fmt.Fprintf(&b, "%v", cc.cands.ValueString())
	case 2:
		fmt.Fprintf(&b, "~%v", cc.cands.ValueString())
	case 3:
		fmt.Fprintf(&b, "%v...", cc.cands.ValueString())
	}
	return b.String()
}

type CondGrid struct {
	cells [9][9]CondCell
}

func makeCondPuzzle(s string, cg *CondGrid, setStrat func(*Grid)) string {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	cellNums := [81]int{}
	for i := 0; i < 81; i++ {
		cellNums[i] = i
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellNums), func(i, j int) {
		cellNums[i], cellNums[j] = cellNums[j], cellNums[i]
	})

	g := Grid{}
	setStrat(&g)
	pre := []byte(s)
	for _, cellNum := range cellNums {
		rowNum, colNum := cellNum/9, cellNum%9
		if cg.cells[rowNum][colNum].op != 0 {
			continue
		}

		cur := slices.Clone(pre)
		cur[cellNum] = '0'
		g.Input(string(cur))
		g.Init()
		g.Clearup()
		if !g.filledCorrenctly() {
			continue
		}
		//used := g.MBacktrack()
		g.Solve()
		if g.Validate() {
			pre = cur
		}
	}

	return s
}

func digCondPuzzle(s string, cg *CondGrid) string {
	r := []byte(s)
	for i := range 9 {
		for j := range 9 {
			num := i*9 + j
			if cg.cells[i][j].op != 0 {
				r[num] = '0'
			}
		}
	}
	return string(r)
}

func (cg *CondGrid) canRemove(rowNum, colNum, value int) bool {
	boxNum, _ := calcBoxAndCellNum(rowNum, colNum)
	for i := range 9 {
		for j := range 9 {
			if cg.cells[i][j].op == 1 {
				cgBoxNum, _ := calcBoxAndCellNum(i, j)
				if i == rowNum || j == colNum || boxNum == cgBoxNum {
					return false
				}
			}
		}
	}
	return true
}
